

module laser_ctrl(
	input				clk,
	input				rst_n,
    //BLUE LASER interface
    input               BLUE_PWM,
    input               BLUE_SW,
	//RED LASER interface	
	input	[7:0]		POWER,	
	input				P9_LATCH,		
	input				P18_EE,		
	input				P19_EM,
	input				P20_SYNC,				
	input				P22_RED,			
	input				P23_ESTOP,	

	output				P16_PD_ALARM,	
	output				P21_TEMP_ALARM,	
	//LASER ALARMS INPUT
    input               VOLT_ALARM,
	input				TEMP_ALARM,
    input               TEMP_SEL,
	input				PD_ALARM,			

	output				DAC1_SW,
	output				DAC1_CLK,
	output	[13:0]		DAC1_DATA,	
	output				DAC2_SW,
	output				DAC2_CLK,
	output	[13:0]		DAC2_DATA,		
	output				DAC3_SW,
	output				DAC3_CLK,
	output	[13:0]		DAC3_DATA,

	output				PUMP_EN1,
	output				PUMP_EN2,
	output				PUMP_EN3,
    output              BLUE_PWR_SEL,
	output				RED_out,
	output				RFA_out,			

	output	            MO_LED,
	output	            BO_LED,
	output				PD_LED,
	output				TEMP_LED
);
localparam  [ 7:0]  CLK_FREQ        = 8'd100;   //100MHz
localparam  [ 7:0]  EN_DELAY        = 8'd5;     //ms
localparam  [ 7:0]  SW_DELAY        = 8'd100;   //us
localparam  [ 7:0]  RED_DELAY       = 8'd10;    //ms
localparam  [ 7:0]  BLUE_EN_DELAY   = 8'd5;     //ms
localparam  [ 9:0]  BLUE_SW_DELAY   = 10'd500;     //us

localparam  [13:0]  LD1_CUR_MIN     = 14'd5000; 
localparam  [13:0]  LD1_CUR_MAX     = 14'd16383;     
localparam  [13:0]  LD2_CUR_MIN     = 14'd2061; 
localparam  [13:0]  LD2_CUR_MAX     = 14'd16383;   

localparam  [ 3:0]  IDLE_ST     = 4'b0001;
localparam  [ 3:0]  RED_ST      = 4'b0010;
localparam  [ 3:0]  LASER_ST    = 4'b0100;
localparam  [ 3:0]  BLUE_ST     = 4'b1000;

reg [ 7:0]  cnt_1us;
reg [ 9:0]  cnt_1ms;
reg         estop_r0,estop_r1,estop_r2;
reg         ready;
reg         red_r0,red_r1,red_r2,red_ctrl,red_sw;
reg [ 7:0]  red_delay_cnt;
reg         en_r0,en_r1,en_r2,en_ctrl;
reg         sw_r0,sw_r1,sw_r2,sw_ctrl;
reg         laser_en;
reg [ 3:0]  opt_st/* synthesis syn_keep = 1 */;
reg [ 7:0]  en_delay_cnt;
reg [ 7:0]  sw_delay_cnt;
reg         laser_sw;
reg         ld1_sw,ld2_sw;
reg [ 7:0]  ld1_on_cnt;
reg [ 7:0]  ld1_off_cnt;
reg         latch_r0,latch_r1,latch_r2,latch_flag;
reg [ 7:0]  latch_cnt;
reg [ 7:0]  power_r0,power_r1,power_r2,pwr_buf;
reg [ 7:0]  ld1_pwr_buf;
reg [ 7:0]  ld2_pwr_buf;
reg [13:0]  ld1_pwr,ld2_pwr,blue_pwr;
reg         alarm_flag;
reg         blue_r0,blue_r1,blue_r2,blue_ctrl,blue_en,blue_sw;
reg [ 7:0]  blue_en_delay_cnt;
reg [ 9:0]  blue_sw_delay_cnt;
reg [ 1:0]  alarm_out;

wire        pulse_1us;
wire        pulse_1ms;
wire        pd_alarm;
wire        volt_alarm;
wire        temp_alarm;
wire [15:0] pulse_width;
wire [13:0] pwr_out;
wire        run_led;
wire        blue_led;

assign PUMP_EN1     = !ready;
assign PUMP_EN2     = !ready;
assign PUMP_EN3     = !ready;
assign BLUE_PWR_SEL = 1'b1;
assign RED_out      = red_sw;

assign DAC1_SW      = ready;
assign DAC1_CLK     = clk;
assign DAC1_DATA    = ld1_pwr;
assign DAC2_SW      = ready;
assign DAC2_CLK     = clk;
assign DAC2_DATA    = ld2_pwr;
assign DAC3_SW      = ready;
assign DAC3_CLK     = clk;
assign DAC3_DATA    = blue_pwr;

assign MO_LED   = blue_en ? blue_led : (laser_en ? 1'b0 : run_led);
assign BO_LED   = blue_sw ? blue_led : (laser_sw ? 1'b0 : run_led);
assign PD_LED   = (pd_alarm || volt_alarm)  ? 1'b0 : 1'b1;
assign TEMP_LED = (temp_alarm ||volt_alarm) ? 1'b0 : 1'b1;

assign P16_PD_ALARM    = pd_alarm;	
assign P21_TEMP_ALARM  = !temp_alarm;	

/******************pulse 1us******************/
always @ (posedge clk or negedge rst_n)begin
	if(!rst_n)
        cnt_1us <= 8'd0;	   	   
    else if(pulse_1us)	   
        cnt_1us <= 1'b0;	    
    else 
        cnt_1us <= cnt_1us + 1'd1;	
end	

assign pulse_1us = (cnt_1us == CLK_FREQ - 1'b1);

always @ (posedge clk or negedge rst_n)begin
	if(!rst_n)
        cnt_1ms <= 8'd0;	   	   
    else if(pulse_1ms)	   
        cnt_1ms <= 1'b0;	    
    else if(pulse_1us)
        cnt_1ms <= cnt_1ms + 1'd1;	
end	

assign pulse_1ms = (cnt_1ms == 10'd999 && pulse_1us);

/******************laser ready******************/
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        estop_r0 <= 1'b0;
        estop_r1 <= 1'b0;
        estop_r2 <= 1'b0;
    end
    else begin
        estop_r0 <= P23_ESTOP;
        estop_r1 <= estop_r0;
        estop_r2 <= estop_r1;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        ready <= 1'b0;
    else if(estop_r1 == 1'b0 && estop_r2 == 1'b1)
        ready <= 1'b0;
    else if(estop_r1 == 1'b1 && estop_r2 == 1'b0)
        ready <= 1'b1;
    else
        ready <= ready;
end

/******************alarm ctrl******************/
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        alarm_flag <= 1'b0;
    else if(pd_alarm || temp_alarm || volt_alarm)
        alarm_flag <= 1'b1;
    else
        alarm_flag <= alarm_flag;
end

/******************red ctrl******************/
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        red_r0 <= 1'b0;
        red_r1 <= 1'b0;
        red_r2 <= 1'b0;
    end
    else begin
        red_r0 <= P22_RED;
        red_r1 <= red_r0;
        red_r2 <= red_r1;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        red_ctrl <= 1'b0;
    else if(red_r1 == 1'b0 && red_r2 == 1'b1)
        red_ctrl <= 1'b0;
    else if(red_r1 == 1'b1 && red_r2 == 1'b0)
        red_ctrl <= 1'b1;
    else
        red_ctrl <= red_ctrl;
end

always @(posedge clk)begin
    if(opt_st == RED_ST)begin
        if(red_delay_cnt < RED_DELAY && pulse_1ms)
            red_delay_cnt <= red_delay_cnt + 1'b1;
        else
            red_delay_cnt <= red_delay_cnt;
    end
    else
        red_delay_cnt <= 10'd0;
end

always @(posedge clk)begin
    if(opt_st == RED_ST)
	    red_sw <= (red_delay_cnt >= RED_DELAY);
    else
        red_sw <= 1'b0;
end

/******************laser ctrl******************/
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin	
		en_r0 <= 1'b0;
		en_r1 <= 1'b0;
        en_r2 <= 1'b0;
	end	
    else begin
		en_r0 <= P18_EE;
		en_r1 <= en_r0;
        en_r2 <= en_r1;
	end
end

always @(posedge clk)begin
    if(!ready || blue_ctrl || alarm_flag)
        en_ctrl <= 1'b0;
    else if(en_r1 == 1'b0 && en_r2 == 1'b1)
        en_ctrl <= 1'b0;
    else if(en_r1 == 1'b1 && en_r2 == 1'b0)
        en_ctrl <= 1'b1;
    else
        en_ctrl <= en_ctrl;
end

/******************laser state******************/
always @(posedge clk or negedge rst_n)begin	
	if(!rst_n)
		opt_st <= IDLE_ST;
	else begin
        case(opt_st)
            IDLE_ST : begin
                if(blue_ctrl)
                    opt_st <= BLUE_ST;
                else if(en_ctrl)
                    opt_st <= LASER_ST;
                else if(red_ctrl)
                    opt_st <= RED_ST;
                else
                    opt_st <= opt_st;
            end
            RED_ST : begin
                if(red_ctrl == 1'b0)
                    opt_st <= IDLE_ST;
                else
                    opt_st <= opt_st;
            end
            LASER_ST : begin
                if(en_ctrl == 1'b0)
                    opt_st <= IDLE_ST;
                else
                    opt_st <= opt_st;
            end
            BLUE_ST : begin
                if(blue_ctrl == 1'b0)
                    opt_st <= IDLE_ST;
                else
                    opt_st <= opt_st;
            end
            default : opt_st <= IDLE_ST;
        endcase
    end
end 

/******************laser en******************/
always @(posedge clk)begin
    if(opt_st == LASER_ST && blue_sw == 1'b0)begin
        if(en_delay_cnt < EN_DELAY && pulse_1ms)
            en_delay_cnt <= en_delay_cnt + 1'b1;
        else
            en_delay_cnt <= en_delay_cnt;
    end
    else
        en_delay_cnt <= 8'd0;
end

always @(posedge clk)begin
    if(en_delay_cnt >= EN_DELAY)
        laser_en <= en_ctrl;
    else 
        laser_en <= 1'b0;
end	

/******************laser sw******************/
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin	
		sw_r0 <= 1'b0;
		sw_r1 <= 1'b0;
        sw_r2 <= 1'b0;
	end	
    else begin
		sw_r0 <= P19_EM;
		sw_r1 <= sw_r0;
        sw_r2 <= sw_r1;
	end
end

always @(posedge clk)begin
    if(!ready || blue_ctrl || alarm_flag)
        sw_ctrl <= 1'b0;
    else if(sw_r1 == 1'b0 && sw_r2 == 1'b1)
        sw_ctrl <= 1'b0;
    else if(sw_r1 == 1'b1 && sw_r2 == 1'b0)
        sw_ctrl <= 1'b1;
    else
        sw_ctrl <= sw_ctrl;
end

/******************laser sw******************/
always @(posedge clk)begin
    if(laser_en)begin
        if(sw_delay_cnt < EN_DELAY && pulse_1ms)
            sw_delay_cnt <= sw_delay_cnt + 1'b1;
        else
            sw_delay_cnt <= sw_delay_cnt;
    end
    else
        sw_delay_cnt <= 8'd0;
end

always @(posedge clk)begin
    if(sw_delay_cnt >= EN_DELAY)	
        laser_sw <= sw_ctrl;
    else
        laser_sw <= 1'b0;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        ld1_sw <= 1'b0;
    else if(ld1_off_cnt == SW_DELAY)
        ld1_sw <= 1'b0;
    else if(laser_sw)
        ld1_sw <= 1'b1;
    else
        ld1_sw <= ld1_sw;
end	

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        ld1_on_cnt <= 8'd0;
    else if(laser_sw == 1'b0)
        ld1_on_cnt <= 8'd0;
    else if(pulse_1us && ld1_on_cnt < SW_DELAY)
        ld1_on_cnt <= ld1_on_cnt + 1'd1;
    else
        ld1_on_cnt <= ld1_on_cnt;
end

always @(posedge clk or negedge rst_n)begin	
	if(!rst_n)
		ld1_off_cnt	<='d0;
	else if(laser_sw)
        ld1_off_cnt	<= 'd0;
	else if(pulse_1us && ld1_off_cnt < SW_DELAY)
        ld1_off_cnt <= ld1_off_cnt + 1'd1;
	else
		ld1_off_cnt <= ld1_off_cnt;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        ld2_sw <= 1'b0;
    else if(laser_sw == 1'b0)
        ld2_sw <= 1'b0;
    else if(ld1_on_cnt == SW_DELAY)
        ld2_sw <= 1'b1;
    else
        ld2_sw <= ld2_sw;
end	

/******************blue ctrl******************/
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        blue_r0 <= 1'b0;
        blue_r1 <= 1'b0;
        blue_r2 <= 1'b0;
    end
    else begin
        blue_r0 <= BLUE_SW;
        blue_r1 <= blue_r0;
        blue_r2 <= blue_r1;
    end
end

always @(posedge clk)begin
    if(!ready || alarm_flag)
        blue_ctrl <= 1'b0;
    else if(blue_r1 == 1'b0 && blue_r2 == 1'b1)
        blue_ctrl <= 1'b0;
    else if(blue_r1 == 1'b1 && blue_r2 == 1'b0)
        blue_ctrl <= 1'b1;
    else
        blue_ctrl <= blue_ctrl;
end

always @(posedge clk)begin
    if(opt_st == BLUE_ST && ld1_sw == 1'b0)begin
        if(pulse_1ms && blue_en_delay_cnt < BLUE_EN_DELAY)
            blue_en_delay_cnt <= blue_en_delay_cnt + 1'd1;
        else
            blue_en_delay_cnt <= blue_en_delay_cnt;
    end
    else
        blue_en_delay_cnt <= 10'd0;
end

always @(posedge clk)begin
    if(blue_en_delay_cnt >= BLUE_EN_DELAY)
	    blue_en <= blue_ctrl;
    else
        blue_en <= 1'b0;
end

always @(posedge clk)begin
    if(blue_en)begin
        if(pulse_1us && blue_sw_delay_cnt < BLUE_SW_DELAY)
            blue_sw_delay_cnt <= blue_sw_delay_cnt + 1'd1;
        else
            blue_sw_delay_cnt <= blue_sw_delay_cnt;
    end
    else
        blue_sw_delay_cnt <= 10'd0;
end

always @(posedge clk)begin
    if(blue_sw_delay_cnt >= BLUE_SW_DELAY)
	    blue_sw <= 1'b1;
    else
        blue_sw <= 1'b0;
end

/******************power ctrl******************/
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin	
		latch_r0 <= 1'b0;
		latch_r1 <= 1'b0;
        latch_r2 <= 1'b0;

        power_r0 <= 8'd0;
        power_r1 <= 8'd0;
        power_r2 <= 8'd0;
	end	
    else begin
		latch_r0 <= P9_LATCH;
		latch_r1 <= latch_r0;
        latch_r2 <= latch_r1;

        power_r0 <= POWER;
        power_r1 <= power_r0;
        power_r2 <= power_r1;
	end
end

always @(posedge clk or negedge rst_n)begin	
	if(!rst_n)
		pwr_buf <= 8'd0;
	else if(latch_r1 && latch_r2 == 1'b0)
        pwr_buf <= power_r2;
	else
		pwr_buf <= pwr_buf;
end

always @(posedge clk or negedge rst_n)begin	
	if(!rst_n)
		ld1_pwr_buf <= 8'd0;
	else
		ld1_pwr_buf <= pwr_buf;
end

always @(posedge clk or negedge rst_n)begin	
	if(!rst_n)
		ld2_pwr_buf <= 8'd38;
	else if(pwr_buf > 8'd38)
		ld2_pwr_buf <= pwr_buf - 8'd38;
end

always @(posedge clk)begin	
    if(ld1_sw)begin
        if(pwr_buf >= 8'd38)
            ld1_pwr <= LD1_CUR_MAX;
        else
            ld1_pwr <= LD1_CUR_MIN + (ld1_pwr_buf << 8) + (ld1_pwr_buf << 5) + (ld1_pwr_buf << 4) + (ld1_pwr_buf << 3);//power 0-15% to cur 30-100% --> 0-38 to 5000-16383 --> power*300  
    end
    else if(laser_en)
        ld1_pwr <= LD1_CUR_MIN;    
    else
        ld1_pwr <= 14'd0;
end

always @(posedge clk)begin	
    if(ld2_sw)begin
        if(pwr_buf <= 8'd38)
            ld2_pwr <= LD2_CUR_MIN;
        else
            ld2_pwr <= LD2_CUR_MIN + (ld2_pwr_buf << 6) + (ld2_pwr_buf << 1);
    end
    else if(laser_en)
        ld2_pwr <= LD2_CUR_MIN;   
    else
        ld2_pwr <= 14'd0;
end

always @(posedge clk)begin	
    if(blue_sw)
		blue_pwr <= pwr_out;
    else if(blue_en)
        blue_pwr <= 14'd300;
    else
        blue_pwr <= 14'd0;
end

/****************blue pwm to pwr******************/
pwm_detect  pwm_detect_inst(
    .clk        (clk),
    .rst_n      (rst_n),
    .pwm_in     (BLUE_PWM),//2~5Khz
    
    .pwr_out    (pwr_out)
);
/****************pulse width detect******************/
pulse_width_detect  pulse_width_detect(
	.clk			    (clk),
	.rst_n			    (rst_n),

	.en			        (red_r2),
	.sclk				(power_r2[2]),
	.sdi				(power_r2[1]),

	.pulse_width	    (pulse_width)
);

/****************sync cycle detect******************/
sync_cycle_detect   sync_cycle_detect(
    .clk                (clk),
    .rst_n              (rst_n),
    .P20_SYNC 	        (P20_SYNC),
    .TEMP_SEL           (TEMP_SEL), 
    .pulse_width        (pulse_width),                		                     	                    		                  	                  		                  
    .RFA_out            (RFA_out)	
);

/****************alarm ctrl******************/
alarm_ctrl  alarm_ctrl(
    .clk		        (clk),
    .rst_n              (rst_n),
    .alarm_en           (blue_sw || laser_sw),
    .pulse_1us          (pulse_1us),	
    .VOLT_ALARM	        (VOLT_ALARM),
    .TEMP_ALARM	        (TEMP_ALARM),
    .PD_ALARM		    (PD_ALARM),

    .pd_alarm	        (pd_alarm),	
    .volt_alarm	        (volt_alarm),	
    .temp_alarm	        (temp_alarm)	
		
);

/****************led ctrl******************/
led_ctrl#(
    .RUN_CYCLE      (1000_000),     //us
    .BLUE_CYCLE     (200_000),      //us
	.CLK_FREQ       (100)           //MHz
)
led_ctrl(
	.clk            (clk	    ),
	.rst_n          (rst_n		),

	.run_led        (run_led    ),
    .blue_led       (blue_led  )
);

endmodule


